#include "GameController.h"

#include <algorithm>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>


// thread om te kijken of speler ready is
void *checkSpelerThread(void *arg) {

	// arg naar speler omzetten
	SpelerContext * temp = (SpelerContext*)arg;

	// lock de mutex
	pthread_mutex_lock(temp->getMutex());

	// check status
	if (temp->getStatus() == 0) {
		//wachten op signal...
		pthread_cond_wait(temp->getCondMutex(), temp->getMutex());
	}

	// unlocken: status OK
	pthread_mutex_unlock(temp->getMutex());
	
	return NULL;
}

GameController::GameController(Spel * parent)
{
	m_parent = parent;

	m_uitvoerLijst = new vector<Kaart*>;

	/* reset flags */
	winnaar_Flag = 0;
	allReady_Flag = 0;

}


void GameController::volgendeRonde()
{
	// Verdeel de kaarten
	verdeelKaarten();

	// Wachten op status
	checkSpelersReady();

	
	for(int i = 0; i < 5; i++)
	{
		//onvangt de kaart uit het ide register
		ontvangKaarten(i);

		//sorteren van de kaarten
		sorteerKaarten();

		//uitvoeren van de kaarten
		voerKaartenUit();

	}	

}

/* Verdeelt de kaarten */
void GameController::verdeelKaarten() 
{
	this->notify();
}

/* Maakt threads aan... */
void GameController::checkSpelersReady()
{
	pthread_t*threads;
	threads = (pthread_t *) malloc(6 * sizeof(*threads));

	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

	int i = 0;

	/* setting up threads */
	for( set< Observer* >::iterator it = m_observers.begin(); it != m_observers.end(); it++ )
	{
		// maak thread aan die verwijst naar de Speler
		if ( pthread_create(&threads[i], &attr, checkSpelerThread, (void *)&it ) != 0 ) {
			abort();
		}

		i++;
	}

	/* synchronizing threads */
	for (size_t i = 0; i < m_observers.size(); i++)
	{
		pthread_join(threads[i], NULL);
	}

	// nu zouden alle spelers klaar moeten zijn

}

Kaart * GameController::getRandomKaart()
{
	Kaart * temp;

	srand ( time(NULL) );
	int randomNr = rand() % 7;
	
	switch(randomNr)
	{
		case 0:	temp = new KaartRechts();
			break;

		case 1: temp = new KaartLinks();
			break;

		case 2: temp = new KaartVooruit(1);
			break;

		case 3: temp = new KaartVooruit(2);
			break;

		case 4: temp = new KaartVooruit(3);
			break;

		case 5: temp = new KaartDraaiOm();
			break;

		case 6: temp = new KaartAchteruit();
			break;

		default: temp = new KaartRechts();
			break;
	}

	
	
	return temp;

}

/* Brengt alle kaarten op register (registerNr) in 1 uitvoervector */
void GameController::ontvangKaarten(int registerNr)
{
	SpelerContext * currentSpeler;
	for( set< Observer* >::iterator it = m_observers.begin(); it != m_observers.end(); it++ )
	{
		//steek alle kaarten in de uitvoervector
		currentSpeler = (SpelerContext*)&it;
		this->m_uitvoerLijst->push_back(currentSpeler->getRegisters().at(registerNr));

	}


}

void GameController::sorteerKaarten()
{
	vector<Kaart*> * gesorteerd = new vector<Kaart*>;
	vector<int> kaartVerwijderd;

	while(kaartVerwijderd.size() != m_uitvoerLijst->size() )
	{
		int maxKaart;
		int maxPrioriteit = 0;

		for(size_t i = 0; i < this->m_uitvoerLijst->size(); i++)
		{
			if(m_uitvoerLijst->at(i)->getPrioriteit() > maxPrioriteit)
			{
				int reedsGetroffen = 0;

				for(size_t j = 0; j < kaartVerwijderd.size() && reedsGetroffen == 0; j++)
				{
					if(kaartVerwijderd[j] == i)
						reedsGetroffen = 1;
				}

				if(reedsGetroffen == 0)
				{
					maxPrioriteit = m_uitvoerLijst->at(i)->getPrioriteit();
					maxKaart = i;
					kaartVerwijderd.push_back(i);
				}

			}
		}

		gesorteerd->push_back(this->m_uitvoerLijst->at(maxKaart));
	}

	delete m_uitvoerLijst;

	m_uitvoerLijst = gesorteerd;


}

/* Alle kaarten uitvoeren */
void GameController::voerKaartenUit()
{
	for(size_t i = 0; i < this->m_uitvoerLijst->size(); i++)
	{
		// de kaart doet zijn acties op de robot
		// de kaart zorgt er ook voor dat de schade van de robot aangepast wordt.
		(*m_uitvoerLijst)[i]->bestuurRobot( &(*m_uitvoerLijst)[i]->getEigenaar()->getRobot(), m_parent );
	}

}


void GameController::addSpelerContext( SpelerContext* context )
{
	this->attach( context );
}

void GameController::removeSpelerContext(SpelerContext * context)
{
	this->detach(context);
}

int GameController::checkWinnaar()
{
	return winnaar_Flag;
}
